package se.sll.itintegration.catalogue.gui.service.view.capacity;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.naming.InitialContext;
import javax.naming.NamingException;

import lombok.extern.slf4j.Slf4j;

import org.apache.camel.Body;
import org.apache.camel.Exchange;
import org.apache.camel.Property;

import se.sll.itintegration.catalogue.gui.data.Capacity;
import se.sll.itintegration.catalogue.gui.data.CapacityResponse;
import se.sll.itintegration.catalogue.gui.service.HsaCache;
import se.sll.service.catalogue.common.rest.model.CodeSystemCode;
import se.sll.service.catalogue.common.rest.model.Product;
import se.sll.service.catalogue.common.rest.model.ProductReference;
import se.sll.service.catalogue.common.rest.model.Unit;
import se.sll.service.catalogue.common.rest.response.HsaData;

/**
 *
 * @author Christer Palm, Nogui
 */
@Slf4j
public class CapacityViewProcessor {
    private static final CapacityViewProcessor INSTANCE = new CapacityViewProcessor();

    private final HsaCache hsaCache;

    private CapacityViewProcessor() {
        try {
            InitialContext context = new InitialContext();
            hsaCache = (HsaCache) context.lookup("java:module/GUI-HSACache");
        } catch (NamingException ex) {
            throw new RuntimeException(ex);
        }
    }

    public void create(Exchange exchange) {
        exchange.setProperty("view", new CapacityView());
    }

    public List<CapacityViewUnit> merge(Exchange exchange, @Body List<Exchange> grouped) {
        // Exchange 1 - offers
        // Holds the list of units to work with and the complete list of
        // offered products for each unit regardless of whether that
        // product has reported capacities or not
        @SuppressWarnings("unchecked")
        List<Unit> offers = grouped.get(0).getIn().getBody(List.class);

        // Exchange 2 - productsById
        // Holds the product names
        @SuppressWarnings("unchecked")
        Map<String, Product> productsById = grouped.get(1).getIn().getBody(Map.class);

        // Exchange 3 - capacities
        // Holds the list of reported capacities of each unit/product
        @SuppressWarnings("unchecked")
        List<CapacityResponse> capacities = grouped.get(2).getIn().getBody(List.class);
        Map<String, Map<String, List<Capacity>>> capacitiesByUnitAndProduct = mapCapacitiesByUnitAndProduct(capacities);

        List<CapacityViewUnit> viewUnits = new ArrayList<>();
        // For each unit in the result set...
        for (Unit srcUnit : offers) {
            CapacityViewUnit viewUnit = new CapacityViewUnit();
            String hsaId = srcUnit.getUnitId();
            viewUnit.setHsaID(hsaId);

            // Set unit name from HSA cache
            viewUnit.setName(getHSAName(hsaId));

            Map<String, List<Capacity>> capacitiesByProduct = capacitiesByUnitAndProduct.get(hsaId);
            List<CapacityViewProduct> viewProducts = new ArrayList<>();
            // For each product the unit offers...
            for (ProductReference srcProductRef : srcUnit.getProductReferences()) {
                CapacityViewProduct viewProduct = ProductReferenceTranslator.getInstance().translate(srcProductRef);
                String productId = viewProduct.getId();
                Product srcProduct = productsById.get(productId);
                if(srcProduct == null) {
                    continue;
                }

                // Add all capacity codes that CAN be reported on the product
                List<CodeSystemCode> srcProductCodes = srcProduct.getCodes();
                CodeSystemCodeCapacityTranslator.getInstance().translateTo(srcProductCodes, viewProduct);

                // Enrich with capacity information from capacities
                List<Capacity> cl = null;
                if (capacitiesByProduct != null) {
                    cl = capacitiesByProduct.get(productId);
                }
                CapacityTranslator.getInstance().translateTo(cl, viewProduct);

                // Set product name from productsById
                viewProduct.setName(srcProduct.getDisplayName());

                viewProducts.add(viewProduct);
            }
            viewUnit.setProducts(viewProducts);
            viewUnits.add(viewUnit);
        }
        return viewUnits;
    }

    public void setCapacity(Exchange exchange, @Property("view") CapacityView view, @Body List<CapacityViewUnit> capacity) {
        log.debug("setCapacity");
        view.setCapacity(capacity);
    }

    private Map<String, Map<String, List<Capacity>>> mapCapacitiesByUnitAndProduct(List<CapacityResponse> capacityResponseList) {
        Map<String, Map<String, List<Capacity>>> capacitiesByUnitAndProduct = new HashMap<>();
        for (CapacityResponse capacityResponse : capacityResponseList) {
            Map<String, List<Capacity>> capacitiesByProduct = new HashMap<>();
            for (se.sll.itintegration.catalogue.gui.data.Product product : capacityResponse.getProduct()) {
                List<Capacity> capacities = product.getCapacity();
                capacitiesByProduct.put(product.getName(), capacities);
            }
            capacitiesByUnitAndProduct.put(capacityResponse.getHsaID(), capacitiesByProduct);
        }
        return capacitiesByUnitAndProduct;
    }

    private String getHSAName(String hsaID) {
        HsaData hsaData = hsaCache.getHsa(hsaID);
        if (hsaData == null) {
            return "";
        }
        return hsaData.getRelativeDistinguishedName();
    }

    public static CapacityViewProcessor getInstance() {
        return INSTANCE;
    }
}
